// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-08-20

package gen_annotation

import (
	"fmt"
	"gitee.com/go-libs/console"
	"html/template"
	"regexp"
	"sort"
	"time"
)

type (
	// ResultController
	// is a component for crontab annotation result.
	ResultController struct {
		Controllers      []*ResultControllerItem
		Package          string
		PackageList      []*ResultControllerAlias
		Script, Datetime template.HTML

		aliasList   []string
		aliasMapper map[string]string
		aliasOffset int
	}

	// ResultControllerAlias
	// is a component for package imports.
	ResultControllerAlias struct {
		Alias string // a0
		Pkg   string // sketch/app/crontabs
	}

	// ResultControllerItem
	// is a component for crontab executors.
	ResultControllerItem struct {
		Line        int      // e.g. 24
		File        string   // e.g. sketch/app/controllers/home_controller.go
		Pkg         string   // e.g. a0
		Struct      string   // e.g. ExampleCrontab
		Middlewares []string // e.g. []string{"example"}
		Prefix      string   // e.g. /
	}
)

// NewResultController
// creates a crontab result component.
func NewResultController(command *console.Command, pkg string) *ResultController {
	return (&ResultController{
		Controllers: make([]*ResultControllerItem, 0),
		Package:     pkg,
		PackageList: make([]*ResultControllerAlias, 0),
	}).init(command)
}

// AddEntity
// add a crontab entity.
func (o *ResultController) AddEntity(entity *EntityController) {
	o.Controllers = append(o.Controllers, &ResultControllerItem{
		Line:        entity.Line,
		File:        entity.File,
		Pkg:         o.AddPackage(entity.Import, true),
		Struct:      entity.Struct,
		Prefix:      entity.Prefix,
		Middlewares: entity.Middlewares,
	})
}

// AddPackage
// add a package import.
func (o *ResultController) AddPackage(pkg string, rename bool) (alias string) {
	pkg = regexp.MustCompile(`/+`).ReplaceAllString(pkg, `/`)

	// Reuse
	// previous alias name.
	if str, ok := o.aliasMapper[pkg]; ok {
		alias = str
		return
	}

	// Alias name generate.
	if rename {
		alias = fmt.Sprintf(`a%d`, o.aliasOffset)
		o.aliasOffset++
	}

	// Update alias mapping and list.
	o.aliasList = append(o.aliasList, pkg)
	o.aliasMapper[pkg] = alias

	// Update package info.
	return
}

// Ready
// is a hook when result ready.
func (o *ResultController) Ready() *ResultController {
	o.initPackages()
	return o
}

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *ResultController) init(command *console.Command) *ResultController {
	// Init
	// internal fields.
	o.aliasList = make([]string, 0)
	o.aliasMapper = make(map[string]string)

	// Init
	// basic fields.
	o.Datetime = template.HTML(time.Now().Format(time.RFC3339))
	o.Script = template.HTML(command.ToScript())

	// Add
	// builtin package.
	o.AddPackage("gitee.com/go-wares/framework-iris/framework", false)
	return o
}

func (o *ResultController) initPackages() {
	// Sort
	// package with alpha number.
	sort.Strings(o.aliasList)

	// Iterate
	// added packages then build package list.
	for _, pkg := range o.aliasList {
		// Build
		// alias component.
		item := &ResultControllerAlias{Pkg: pkg}

		// Build
		// alias name.
		if alias, ok := o.aliasMapper[pkg]; ok {
			item.Alias = alias
		}

		// Add
		// to list.
		o.PackageList = append(o.PackageList, item)
	}
}
